package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"mall/common/modelx"
	"mall/service/forum/model"

	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	// ForumReactionRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customForumReactionRepository.
	ForumReactionRepository interface {
		Conn() error
		GetList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]interface{},
		) ([]*model.ForumReaction, int64, error)
		GetLogs(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]interface{},
		) ([]*model.ForumReactionLog, int64, error)
		Insert(
			ctx context.Context,
			reaction *model.ForumReaction,
		) error
		InsertLog(
			ctx context.Context,
			log *model.ForumReactionLog,
		) error
		UpdateBatch(
			ctx context.Context,
			cols model.M,
			filter map[string]map[string][]interface{},
		) (int64, error)
		ForceDeleteBatch(ctx context.Context, ids []int64) (int64, error)
	}

	customForumReactionRepository struct {
		cacheConf  cache.CacheConf
		dataSource string

		mysqlConn *sql.DB
	}
)

// NewForumReactionRepository returns a repository for the database table.
func NewForumReactionRepository(dataSource string, cacheConf cache.CacheConf) ForumReactionRepository {
	return &customForumReactionRepository{
		cacheConf:  cacheConf,
		dataSource: dataSource,
	}
}

func (r *customForumReactionRepository) Conn() error {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return err
		}

		r.mysqlConn = mysqlConn
	}

	return nil
}

func (r *customForumReactionRepository) GetList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]interface{},
) (list []*model.ForumReaction, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.ForumReactionColumns.ReactionID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionID, opMap)...)
			case model.ForumReactionColumns.ForumID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ForumID, opMap)...)
			case model.ForumReactionColumns.TopicID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.TopicID, opMap)...)
			case model.ForumReactionColumns.PostID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.PostID, opMap)...)
			case model.ForumReactionColumns.AuthorID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.AuthorID, opMap)...)
			case model.ForumReactionColumns.ReactionUserID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionUserID, opMap)...)
			case model.ForumReactionColumns.ReactionUser:
				mods = append(mods, modelx.GetStringMods(model.ForumReactionColumns.ReactionUser, opMap)...)
			case model.ForumReactionColumns.ReactionType:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionType, opMap)...)
			case model.ForumReactionColumns.ReactionStatus:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionStatus, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.ForumReactions(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.ForumReactionColumns.ReactionID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.ForumReactions(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customForumReactionRepository) GetLogs(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]interface{},
) (list []*model.ForumReactionLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	var latest bool
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		case "latest":
			if val, ok := v.(string); ok && val == "1" {
				latest = true
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.ForumReactionLogColumns.ID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionLogColumns.ID, opMap)...)
			case model.ForumReactionLogColumns.TopicID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionLogColumns.TopicID, opMap)...)
			case model.ForumReactionLogColumns.PostID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionLogColumns.PostID, opMap)...)
			case model.ForumReactionLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionLogColumns.UserID, opMap)...)
			case model.ForumReactionLogColumns.Type:
				mods = append(mods, modelx.GetStringMods(model.ForumReactionLogColumns.Type, opMap)...)
			case model.ForumReactionLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionLogColumns.Status, opMap)...)
			}
		}
	}

	if latest {
		mods = append(mods, qm.Select(fmt.Sprintf(
			"max(%s) as %s",
			model.ForumReactionLogColumns.ID,
			model.ForumReactionLogColumns.ID,
		)))
		mods = append(mods, qm.GroupBy(model.ForumReactionLogColumns.PostID))

		preList, listErr := model.ForumReactionLogs(mods...).All(ctx, r.mysqlConn)
		if listErr != nil {
			err = listErr
			return
		}

		if len(preList) > 0 {
			ids := make([]uint64, 0, len(preList))
			for _, log := range preList {
				ids = append(ids, log.ID)
			}
			var subMods []qm.QueryMod
			subMods = append(subMods, model.ForumReactionLogWhere.ID.IN(ids))
			// subMods = append(subMods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.ForumReactionLogColumns.PostID), ids...))
			
			var columns []string
			if len(selectCol) > 0 {
				if selectCol == "," {
					columns = append(columns, model.ForumReactionLogColumns.ID)
				} else {
					columns = strings.Split(selectCol, ",")
				}
			}

			if len(columns) > 0 {
				subMods = append(subMods, qm.Select(columns...))
			}
			list, err = model.ForumReactionLogs(subMods...).All(ctx, r.mysqlConn)
			return
		}

		return
	}

	if withCount >= 0 {
		count, err = model.ForumReactionLogs(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.ForumReactionLogColumns.ID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.ForumReactionLogs(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customForumReactionRepository) InsertLog(
	ctx context.Context,
	log *model.ForumReactionLog,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return log.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customForumReactionRepository) Insert(
	ctx context.Context,
	reaction *model.ForumReaction,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return reaction.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customForumReactionRepository) UpdateBatch(
	ctx context.Context,
	cols model.M,
	filter map[string]map[string][]interface{},
) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.ForumReactionColumns.ReactionID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionID, opMap)...)
			case model.ForumReactionColumns.ForumID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ForumID, opMap)...)
			case model.ForumReactionColumns.TopicID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.TopicID, opMap)...)
			case model.ForumReactionColumns.PostID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.PostID, opMap)...)
			case model.ForumReactionColumns.AuthorID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.AuthorID, opMap)...)
			case model.ForumReactionColumns.ReactionUserID:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionUserID, opMap)...)
			case model.ForumReactionColumns.ReactionUser:
				mods = append(mods, modelx.GetStringMods(model.ForumReactionColumns.ReactionUser, opMap)...)
			case model.ForumReactionColumns.ReactionType:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionType, opMap)...)
			case model.ForumReactionColumns.ReactionStatus:
				mods = append(mods, modelx.GetIntMods(model.ForumReactionColumns.ReactionStatus, opMap)...)
			}
		}
	}
	if len(mods) == 0 {
		return
	}

	rowsAff, err = model.ForumReactions(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customForumReactionRepository) ForceDeleteBatch(ctx context.Context, ids []int64) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]interface{}, 0, len(ids))
		for _, id := range ids {
			values = append(values, id)
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.ForumReactionColumns.ReactionID), values...))
	}

	rowsAff, err = model.ForumReactions(mods...).DeleteAll(ctx, r.mysqlConn)
	return
}
